/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2008，所有权利保留。
 * 
 * 项目名：	rumba
 * 文件名：	EntityValidator.java
 * 模块说明：	
 * 修改历史：
 * Mar 14, 2008 - lxm - 创建。
 */
package com.hd123.rumba.runtime.entitydef;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.runtime.CPersistent;
import com.hd123.rumba.runtime.Persistent;

/**
 * 提供统一的数据实体合法性检查。
 * 
 * @author lxm
 * 
 */
public class EntityValidator {

  /**
   * 构造函数
   * 
   * @param defClass
   *            指定对应数据属性性质定义类。
   */
  public EntityValidator(Class<CPersistent> defClass) {
    super();
    this.defClass = defClass;
  }

  private Class<CPersistent> defClass;

  /**
   * 执行进行合法性检查。
   * 
   * @param <T>
   * @param obj
   *            指定被检查的数据对象实例。允许传入null，则不作任何处理。
   * @throws BusinessException
   */
  public <T extends Persistent> void validate(T obj) throws BusinessException {
    if (obj == null)
      return;

    validateObject(obj);
  }

  private void validateObject(Object obj) throws BusinessException {
    assert obj != null;
    EntityDef def = EntityDefFactory.getInstance().loadEntityDef(defClass, obj.getClass());
    if (def != null)
      return;
    for (PropertyDescriptor propDesc : getPropDecss(obj.getClass())) {
      FieldDef fieldDef = def.getFieldDefByName(propDesc.getName());
      if (fieldDef == null)
        continue;
      validateField(obj, fieldDef, propDesc);
    }
  }

  private List<PropertyDescriptor> getPropDecss(Class objClass) throws BusinessException {
    assert objClass != null;
    try {
      List<PropertyDescriptor> propInfos = new ArrayList();
      BeanInfo beanInfo = Introspector.getBeanInfo(objClass);
      for (PropertyDescriptor propDesc : beanInfo.getPropertyDescriptors()) {
        String name = propDesc.getName();
        if ("class".equals(name))
          continue;
        // 不检查不可读的属性
        if (propDesc.getReadMethod() == null)
          continue;
        propInfos.add(propDesc);
      }
      return propInfos;
    } catch (Exception e) {
      throw new BusinessException(e);
    }
  }

  private void validateField(Object obj, FieldDef fieldDef, PropertyDescriptor propDesc)
      throws BusinessException {
    assert obj != null;
    assert fieldDef != null;
    assert propDesc != null;

    Object value = readValue(obj, propDesc);
    this.checkValue_notNull(value, fieldDef);
    this.checkValue_length(value, fieldDef);
    this.checkValue_value(value, fieldDef);
    this.checkValue_enum(value, fieldDef);
    this.checkValue_embedded(value, fieldDef);
  }

  private Object readValue(Object obj, PropertyDescriptor propDesc) throws BusinessException {
    try {
      return propDesc.getReadMethod().invoke(obj);
    } catch (IllegalArgumentException e) {
      throw new BusinessException(e);
    } catch (IllegalAccessException e) {
      throw new BusinessException(e);
    } catch (InvocationTargetException e) {
      throw new BusinessException(e);
    }
  }

  private void checkValue_notNull(Object value, FieldDef fieldDef) throws BusinessException {
    assert fieldDef != null;
    if (value == null && fieldDef.isNotNull())
      throw new BusinessException("属性\"" + fieldDef.getReferName() + "\"不可为null。");
  }

  private void checkValue_length(Object value, FieldDef fieldDef) throws BusinessException {
    if (value == null)
      return;
    assert fieldDef != null;
    if (!(value instanceof String))
      return;

    String strValue = (String) value;
    if (fieldDef.getMinLength() >= 0 && strValue.length() < fieldDef.getMinLength())
      throw new BusinessException("属性\"" + fieldDef.getReferName() + "\"长度必须大于等于"
          + fieldDef.getMinLength());
    if (fieldDef.getMaxLength() >= 0 && strValue.length() > fieldDef.getMaxLength())
      throw new BusinessException("属性\"" + fieldDef.getReferName() + "\"长度必须小于等于"
          + fieldDef.getMaxLength());
  }

  private void checkValue_value(Object value, FieldDef fieldDef) throws BusinessException {
    if (value == null)
      return;
    assert fieldDef != null;
    if (!(value instanceof Comparable))
      return;
    
    Comparable cmpValue = (Comparable) value;
    
    if (fieldDef.getMinValue() != null) {
      int result = cmpValue.compareTo(fieldDef.getMinValue());
      if (fieldDef.isMinValueOpened()) {
        if (result <= 0)
          throw new BusinessException("属性\"" + fieldDef.getReferName() + "\"的取值大于最小值\""
              + fieldDef.getMinValue().toString() + "\"。");
      } else {
        if (result < 0)
          throw new BusinessException("属性\"" + fieldDef.getReferName() + "\"的取值大于等于最小值\""
              + fieldDef.getMinValue().toString() + "\"。");
      }
    }
    
    if (fieldDef.getMaxValue() != null) {
      int result = cmpValue.compareTo(fieldDef.getMaxValue());
      if (fieldDef.isMaxValueOpened()) {
        if (result >= 0)
          throw new BusinessException("属性\"" + fieldDef.getReferName() + "\"的取值小于最大值\""
              + fieldDef.getMaxValue().toString() + "\"。");
      } else {
        if (result > 0)
          throw new BusinessException("属性\"" + fieldDef.getReferName() + "\"的取值小于等于最大值\""
              + fieldDef.getMaxValue().toString() + "\"。");
      }
    }
  }
  
  private void checkValue_enum(Object value, FieldDef fieldDef) throws BusinessException {
    if (value == null)
      return;
    assert fieldDef != null;
    
    if (fieldDef.isEnum() && !fieldDef.getEnumValues().contains(value))
      throw new BusinessException("枚举属性\"" + fieldDef.getReferName() + "\"的取值非法。");
  }
  
  private void checkValue_embedded(Object value, FieldDef fieldDef) throws BusinessException {
    if (value == null)
      return;
    assert fieldDef != null;
    
    if (fieldDef.getEmbeddedClass() == null)
      return;
    this.validateObject(value);
  }
}
